﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;
using Microsoft.Office.Interop.Excel;
using System.Data;
using PanGu;
using System.Windows;
using PatientInformationExtract.Components;


namespace PatientInformationExtract.MainInterface.Models.XMLCS
{
    /// <summary>
    /// 病人信息类，管理一个病人文件夹下提取出来的所有属性
    /// </summary>
    public class PatientInfo
    {
        private EventLog _owner;
        public EventLog Owner { get { return this._owner; } }
        private string _patientID;
        public string PatientID { get { return this._patientID; } }
        private string _visitID;
        public string VisitID { get { return this._visitID; } }
        private string _visitID2;
        public string VisitID2 { get { return this._visitID2; } }
        private string _patientName;
        public string PatientName { get { return this._patientName; } }
        private string _gender;
        public string Gender { get { return this._gender; } }
        private string _age;
        public string Age { get { return this._age; } }        
        private DateTime _earliestDate = new DateTime(9999, 1, 1);
        private DateTime? _admitDate;
        public DateTime AdmitDate { 
            get 
            {
                if (this._admitDate != null)
                {
                    return (DateTime)this._admitDate;
                }
                else
                {
                    return this._earliestDate;
                }
            } 
        }
        private DateTime _latestDate = new DateTime(1, 1, 1);
        private DateTime? _dischargeDate;
        public DateTime DischargeDate { 
            get 
            {
                if (this._dischargeDate != null)
                {
                    return (DateTime)this._dischargeDate;
                }
                else
                {
                    return this._latestDate;
                }
            }
        }
        private ParseOption _option;
        public ParseOption Option { get { return this._option; } }
        private List<string> _diagnosis;
        public List<string> Diagnosis { get { return this._diagnosis; } }
        private string _folderPath;
        public string FoderPath { get { return this._folderPath; } }
        private XmlElement _patientTrace;
        public XmlElement PatientTrace { get { return this._patientTrace; } }
        private List<IPatientInfoComponent> _components;
        public List<IPatientInfoComponent> Components { get { return this._components; } }
        private XmlDocument _xml;
        public XmlDocument XMLDoc { get { return this._xml; } }
        private Workbooks _excelWBs;

        public PatientInfo(ref XmlDocument xml, ref Workbooks workBooks)
        {
            this._xml = xml;
            _patientID = null;
            _visitID = null;
            _visitID2 = null;
            _patientName = null;
            _gender = null;
            _age = null;
            _diagnosis = null;
            _folderPath = null;
            _components = new List<IPatientInfoComponent>();
            _option = new ParseOption();

            _components.Add(new LabInfo(xml));
            _components.Add(new ExamInfo(xml));
            _components.Add(new OrdersInfo(xml));
            _components.Add(new VitalSignInfo(xml));
            _components.Add(new ProcessNoteInfo(xml));

            _excelWBs = workBooks;
            _patientTrace = xml.CreateElement("PatientTrace");
        }

        public PatientInfo(ref XmlDocument xml, ref Workbooks workBooks, EventLog owner)
            : this(ref xml, ref workBooks)
        {
            this._owner = owner;
            this._option = owner.Option;
        }

        public void AddNewComponent(IPatientInfoComponent component)
        {
            component.SetXML(ref _xml);
            _components.Add(component);
        }


        /// <summary>
        /// 对病人文件夹内的文件进行解析
        /// </summary>
        /// <param name="folderPath">病人文件夹路径</param>
        public void Initialize(string folderPath)
        {
            this._folderPath = folderPath;
            FileInfo[] files = new DirectoryInfo(folderPath).GetFiles();
            foreach (FileInfo file in files)
            {
                foreach (IPatientInfoComponent component in this._components)
                {
                    bool isToParse = true;
                    if (this.Option.ParseComponentType.Count != 0)
                    {
                        isToParse = false;
                        foreach (Type type in this.Option.ParseComponentType)
                        {
                            if (component.GetType().Equals(type))
                            {
                                isToParse = true;
                            }
                        }
                    }
                    if (isToParse)
                    {
                        if (file.Extension.Equals(".XLS") && component is IExcelInfo)
                        {
                            InitializeIExcelInfo(component as IExcelInfo, file);
                        }
                        if (file.Extension.Equals(".htm") && component is IHtmlInfo)
                        {
                            InitializeIHtmlInfo(component as IHtmlInfo, file);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 对实现接口IExcelInfo的类进行解析，只有文件名与类中定义的文件名相同才会进行解析
        /// </summary>
        /// <param name="excelInfo">要进行解析的类</param>
        /// <param name="file">文件信息</param>
        /// <param name="hdr">从Excel表格的第几行读起</param>
        private void InitializeIExcelInfo(IExcelInfo excelInfo, FileInfo file)
        {
            if (file.Name == excelInfo.FileName)
            {
                excelInfo.Initialize(file.FullName, _excelWBs);
                if (excelInfo.PatientID != null)
                {
                    if (this.PatientID == null)
                    {
                        this._patientID = excelInfo.PatientID;
                    }
                    else
                    {
                        if (this.PatientID != excelInfo.PatientID)
                            throw new Exception(String.Format(PatientInfoErrorMessages.PatientPropertyDisaccord, "PatientID", file.FullName));
                    }
                }
                if (excelInfo.VisitID != null)
                {
                    if (this.VisitID == null)
                    {
                        this._visitID = excelInfo.VisitID;
                    }
                    else
                    {
                        if (this.VisitID != excelInfo.VisitID)
                            throw new Exception(String.Format(PatientInfoErrorMessages.PatientPropertyDisaccord, "VisitID", file.FullName));
                    }
                }
            }
        }

        private void InitializeIHtmlInfo(IHtmlInfo htmlInfo, FileInfo file)
        {
            bool isMatch;
            try
            {
                HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
                doc.Load(file.FullName);
                isMatch = Regex.IsMatch(doc.DocumentNode.SelectSingleNode(@"/html[1]/body[1]/table[1]/tr[1]/td[1]/table[1]/tr[1]/td[1]/p[3]").InnerText.Replace(@"&nbsp;", ""), htmlInfo.FileName);
            }
            catch
            {
                return;
            }
            if (isMatch)
            {
                htmlInfo.Initialize(file.FullName);
                if (htmlInfo.PatientID != null)
                {
                    if (_patientID == null)
                    {
                        _patientID = htmlInfo.PatientID;
                    }
                    else
                    {
                        if (_patientID != htmlInfo.PatientID)
                            throw new Exception(String.Format(PatientInfoErrorMessages.PatientPropertyDisaccord, "PatientID", file.FullName));
                    }
                }
                if (htmlInfo.VisitID2 != null)
                {
                    if (_visitID2 == null)
                    {
                        _visitID2 = htmlInfo.VisitID2;
                    }
                    else
                    {
                        if (_visitID2 != htmlInfo.VisitID2)
                            throw new Exception(String.Format(PatientInfoErrorMessages.PatientPropertyDisaccord, "VisitID2", file.FullName));
                    }
                }
                if (htmlInfo.PatientName != null)
                {
                    if (_patientName == null)
                    {
                        _patientName = htmlInfo.PatientName;
                    }
                    else
                    {
                        if (_patientName != htmlInfo.PatientName)
                            throw new Exception(String.Format(PatientInfoErrorMessages.PatientPropertyDisaccord, "PatientName", file.FullName));
                    }
                }
                if (htmlInfo.Gender != null)
                {
                    if (_gender == null)
                    {
                        _gender = htmlInfo.Gender;
                    }
                    else
                    {
                        if (_gender != htmlInfo.Gender)
                            throw new Exception(String.Format(PatientInfoErrorMessages.PatientPropertyDisaccord, "Gender", file.FullName));
                    }
                }
                if (htmlInfo.Age != null)
                {
                    if (_age == null)
                    {
                        _age = htmlInfo.Age;
                    }
                    else
                    {
                        if (_age != htmlInfo.Age)
                            throw new Exception(String.Format(PatientInfoErrorMessages.PatientPropertyDisaccord, "Age", file.FullName));
                    }
                }
                if (htmlInfo.Diagnosis != null)
                {
                    if (_diagnosis == null)
                    {
                        _diagnosis = htmlInfo.Diagnosis;
                    }
                    else
                    {
                        //throw new Exception("病人文件夹内Diagnosis不一致");
                    }
                }
            }
        }

        /// <summary>
        /// 将各个表格解析出来的数据添加到元素PatientTrace中去
        /// </summary>
        public void Update()
        {
            _patientTrace.RemoveAll();

            if (_patientID != null)
            {
                _patientTrace.SetAttribute("PatientID", _patientID);
            }
            if (_visitID != null)
            {
                _patientTrace.SetAttribute("VisitID", _visitID);
            }
            if (_visitID2 != null)
            {
                _patientTrace.SetAttribute("VisitID2", _visitID2);
            }
            _patientTrace.SetAttribute("PatientName", _patientName);
            if (_gender != null)
            {
                _patientTrace.SetAttribute("Gender", _gender);
            }
            if (_age != null)
            {
                _patientTrace.SetAttribute("Age", _age);
            }
            if (_diagnosis != null)
            {
                _patientTrace.SetAttribute("Diagnosis", String.Join("|", _diagnosis));
            }

            Segment segment = new Segment();

            foreach (IPatientInfoComponent component in this._components)
            {
                foreach (XmlElement element in component.XmlElements)
                {
                    XmlNode nameE = element.SelectSingleNode(".//Name");

                    //Find the earliest and latest date
                    DateTime occurTime = Convert.ToDateTime(element.Attributes["OccurTime"].Value.Replace("T", " ").Replace("Z", ""));
                    if (occurTime < this._earliestDate)
                    {
                        this._earliestDate = occurTime;
                    }
                    if (occurTime > this._latestDate)
                    {
                        this._latestDate = occurTime;
                    }

                    //Delete OriginalSource node
                    XmlNode originalSourceE = element.SelectSingleNode(".//OriginalSource");
                    originalSourceE.ParentNode.RemoveChild(originalSourceE);

                    if (nameE.InnerText.Equals("入院"))
                    {
                        this._admitDate = occurTime;
                    }
                    if (nameE.InnerText.Equals("出院"))
                    {
                        this._dischargeDate = occurTime;
                    }

                    string name = nameE.InnerText.Clone() as string;
                    if (this.Option.IsEncoded)
                    {
                        string encoding = Segment.ConvertWordToEncoding(name);
                        if (encoding != null)
                        {
                            nameE.InnerText = encoding;
                        }
                    }
                    if (this.Option.IsTranslatedToEnglish)
                    {
                        string wordToTranslate = name.Clone() as string;
                        //提取括号内词，如果不是英文或数字则删除
                        if (Regex.IsMatch(wordToTranslate, @"\((.+)\)"))
                        {
                            ICollection<WordInfo> rs = segment.DoSegment(Regex.Match(wordToTranslate, @"\((.+)\)").Groups[1].ToString(), false);
                            foreach (WordInfo word in rs)
                            {
                                if (word.Pos.Equals(POS.POS_UNK) || word.Pos.Equals(POS.POS_A_NR))
                                {
                                    if (!Regex.IsMatch(word.Word, @"^[0-9a-zA-Z-\. ]+$"))
                                    {
                                        wordToTranslate = new Regex(@"\(.*\)").Replace(wordToTranslate, "");
                                    }
                                }
                            }
                        }
                        if (Regex.IsMatch(wordToTranslate, @"（(.+)）"))
                        {
                            ICollection<WordInfo> rs = segment.DoSegment(Regex.Match(wordToTranslate, @"（(.+)）").Groups[1].ToString(), false);
                            foreach (WordInfo word in rs)
                            {
                                if (word.Pos.Equals(POS.POS_UNK) || word.Pos.Equals(POS.POS_A_NR))
                                {
                                    if (!Regex.IsMatch(word.Word, @"^[0-9a-zA-Z-\. ]+$"))
                                    {
                                        wordToTranslate = new Regex(@"（.*\）").Replace(wordToTranslate, "");
                                    }
                                }
                            }
                        }
                        //利用盘古进行分词，如果不识别的部分不是数字则不能直接利用盘古进行翻译
                        ICollection<WordInfo> result = segment.DoSegment(wordToTranslate, false);
                        List<string> unknownSegments = new List<string>();
                        bool canTranslatedToEnglish = true;
                        foreach (WordInfo word in result)
                        {
                            if (word.Pos.Equals(POS.POS_UNK))
                            {
                                if (!Regex.IsMatch(word.Word, @"^[0-9a-zA-Z-\. ]+$"))
                                {
                                    canTranslatedToEnglish = false;
                                    unknownSegments.Add(word.Word);
                                }
                            }
                            else if (word.Pos.Equals(POS.POS_SYMBOL))
                            {
                            }
                            else
                            {
                                if (String.IsNullOrEmpty(word.English))
                                {
                                    canTranslatedToEnglish = false;
                                    unknownSegments.Add(word.Word);
                                }
                            }
                        }
                        //如果能翻译，直接调用盘古词典进行翻译
                        if (canTranslatedToEnglish)
                        {
                            XmlElement englishE = element.OwnerDocument.CreateElement("English");
                            List<string> buffer = new List<string>();
                            foreach (WordInfo word in result)
                            {
                                string temp;
                                if (word.English != null)
                                {
                                    temp = word.English;
                                }
                                else
                                {
                                    temp = word.Word;
                                }
                                buffer.Add(temp.Trim());
                            }
                            englishE.InnerText = String.Join(" ", buffer);
                            element.InsertAfter(englishE, nameE);
                        }
                        else
                        {
                            if (this.Option.IsUsedTranslator)
                            {
                                XmlElement englishE = element.OwnerDocument.CreateElement("English");
                                string translation;
                                //如果翻译学习词典中有则直接翻译，如果无则调用微软翻译接口翻译
                                if (Singleton<MainInterfaceModel>.Instance.TranslationLearningDict.TryGetTranslation(wordToTranslate, out translation))
                                {
                                    englishE.InnerText = translation;
                                }
                                else
                                {
                                    //translation = Singleton<MainInterfaceModel>.Instance.CMLPDictHelper.TryGetTranslation(wordToTranslate);
                                    if (String.IsNullOrEmpty(translation))
                                    {
                                        translation = Singleton<MainInterfaceModel>.Instance.Translator.Translate(wordToTranslate, "zh-CHS", "en");
                                    }
                                    else
                                    {
                                        translation += "*";
                                    }
                                    Logger.Translate("At " + this._folderPath + ", word:" + name + ", unknown segments: " + String.Join(",", unknownSegments), Logger.LogStatus.Failure);
                                    if (String.IsNullOrEmpty(translation))
                                    {
                                        Logger.Translate("Mircosoft translator time out.", Logger.LogStatus.Failure);
                                        englishE.InnerText = "*";
                                    }
                                    else
                                    {
                                        Logger.Translate(name + " translates to " + translation + ".", Logger.LogStatus.Succeed);
                                        Singleton<MainInterfaceModel>.Instance.TranslationLearningDict.AddNewWord(wordToTranslate, translation, unknownSegments);
                                        englishE.InnerText = translation;
                                    }
                                }
                                element.InsertAfter(englishE, nameE);
                            }
                        }
                    }
                    if (this.Option.IsSynonymReplaced)
                    {
                        ICollection<WordInfo> result = segment.DoSegment(name, true);
                        bool existsSynonym = false;
                        foreach (WordInfo word in result.ToArray())
                        {
                            if (word.WordType == WordType.Synonym)
                            {
                                existsSynonym = true;
                            }
                        }
                        if (existsSynonym)
                        {
                            //
                        }
                    }
                    _patientTrace.AppendChild(element);
                }
            }

            _patientTrace.SetAttribute("AdmitDate", this.AdmitDate.ToString("yyyy-MM-ddTHH:mm:ssZ"));
            _patientTrace.SetAttribute("DischargeDate", this.DischargeDate.ToString("yyyy-MM-ddTHH:mm:ssZ"));
        }

        /// <summary>
        /// 将获取到的信息转换成DataTable用于在DataGrid中显示
        /// </summary>
        /// <returns>返回用于显示的DataTable</returns>
        public System.Data.DataTable GetObservationTable()
        {
            System.Data.DataTable dt = new System.Data.DataTable("Observation");
            dt.Columns.Add("OccurTime", System.Type.GetType("System.String"));
            dt.Columns.Add("Type", System.Type.GetType("System.String"));
            dt.Columns.Add("Name", System.Type.GetType("System.String"));
            dt.Columns.Add("English", System.Type.GetType("System.String"));
            dt.Columns.Add("Result", System.Type.GetType("System.String"));
            dt.Columns.Add("Unit", System.Type.GetType("System.String"));
            dt.Columns.Add("Abnormal", System.Type.GetType("System.String"));
            XmlNodeList nodeList = _patientTrace.SelectNodes(".//Observation");
            if (nodeList.Count != 0)
            {
                foreach (XmlNode node in nodeList)
                {
                    DataRow dr = dt.NewRow();
                    string time = RemoveTZ(node.Attributes["OccurTime"].Value.ToString());
                    dr["OccurTime"] = time;
                    dr["Type"] = node.Attributes["Type"].Value.ToString();
                    foreach (XmlNode node1 in node.ChildNodes)
                    {
                        switch (node1.Name)
                        {
                            case "Name":
                                {
                                    if (this.Option.IsEncoded)
                                    {
                                        string word = Segment.ConvertEncodingToWord(node1.InnerText);
                                        if (word != null)
                                        {
                                            dr["Name"] = word;
                                        }
                                        else
                                        {
                                            dr["Name"] = node1.InnerText;
                                        }
                                    }
                                    else
                                    {
                                        dr["Name"] = node1.InnerText;
                                    }
                                    break;
                                }
                            case "Result":
                                {
                                    dr["Result"] = node1.InnerText;
                                    if (node1.Attributes.Count != 0)
                                    {
                                        dr["Unit"] = node1.Attributes["Unit"].Value.ToString();
                                    }
                                    break;
                                }
                            case "Abnormal":
                                {
                                    dr["Abnormal"] = node1.InnerText;
                                    break;
                                }
                            case "English":
                                {
                                    dr["English"] = node1.InnerText;
                                    break;
                                }

                        }
                    }
                    dt.Rows.Add(dr);
                }
            }
            else
            {
                dt = null;
            }
            return dt;
        }

        /// <summary>
        /// 将获取到的信息转换成DataTable用于在DataGrid中显示
        /// </summary>
        /// <returns>返回用于显示的DataTable</returns>
        public System.Data.DataTable GetMedicalInterventionTable()
        {
            System.Data.DataTable dt = new System.Data.DataTable("MedicalIntervention");
            dt.Columns.Add("OccurTime", System.Type.GetType("System.String"));
            dt.Columns.Add("Type", System.Type.GetType("System.String"));
            dt.Columns.Add("Name", System.Type.GetType("System.String"));
            dt.Columns.Add("English", System.Type.GetType("System.String"));
            dt.Columns.Add("StartTime", System.Type.GetType("System.String"));
            dt.Columns.Add("EndTime", System.Type.GetType("System.String"));
            dt.Columns.Add("Amount", System.Type.GetType("System.String"));
            dt.Columns.Add("Unit", System.Type.GetType("System.String"));
            dt.Columns.Add("Frequency", System.Type.GetType("System.String"));
            dt.Columns.Add("Duration", System.Type.GetType("System.String"));
            dt.Columns.Add("OrderBy", System.Type.GetType("System.String"));
            dt.Columns.Add("PerformBy", System.Type.GetType("System.String"));
            dt.Columns.Add("Parameter", System.Type.GetType("System.String"));
            dt.Columns.Add("Description", System.Type.GetType("System.String"));
            dt.Columns.Add("Impression", System.Type.GetType("System.String"));
            XmlNodeList nodeList = _patientTrace.SelectNodes(".//MedicalIntervention");
            if (nodeList.Count != 0)
            {
                foreach (XmlNode node in nodeList)
                {
                    DataRow dr = dt.NewRow();
                    string time = RemoveTZ(node.Attributes["OccurTime"].Value.ToString());
                    dr["OccurTime"] = time;
                    dr["Type"] = node.Attributes["Type"].Value.ToString();
                    if (this.Option.IsEncoded)
                    {
                        string word = Segment.ConvertEncodingToWord(node.ChildNodes[0].InnerText);
                        if (word != null)
                        {
                            dr["Name"] = word;
                        }
                        else
                        {
                            dr["Name"] = node.ChildNodes[0].InnerText;
                        }
                    }
                    else
                    {
                        dr["Name"] = node.ChildNodes[0].InnerText;
                    }
                    foreach (XmlNode node1 in node.ChildNodes)
                    {
                        switch (node1.Name)
                        {
                            case "Result":
                                {
                                    foreach (XmlNode node11 in node1.ChildNodes)
                                    {
                                        if (node11.Name.Equals("Numberic"))
                                        {
                                            dr["Parameter"] = node11.InnerText;
                                        }
                                        if (node11.Name.Equals("Literal"))
                                        {

                                            if (node11.Attributes[0].Value.ToString().Equals("Description"))
                                            {
                                                dr["Description"] = node11.InnerText;
                                            }
                                            if (node11.Attributes[0].Value.ToString().Equals("Impression"))
                                            {
                                                dr["Impression"] = node11.InnerText;
                                            }
                                        }
                                    }
                                    break;
                                }
                            case "StartTime":
                                {
                                    dr["StartTime"] = RemoveTZ(node1.InnerText);
                                    break;
                                }
                            case "EndTime":
                                {
                                    dr["EndTime"] = RemoveTZ(node1.InnerText);
                                    break;
                                }
                            case "Amount":
                                {
                                    dr["Amount"] = node1.InnerText;
                                    if (node1.Attributes.Count != 0)
                                    {
                                        dr["Unit"] = node1.Attributes["Unit"].Value.ToString();
                                    }
                                    break;
                                }
                            case "Frequency":
                                {
                                    dr["Frequency"] = node1.InnerText;
                                    if (node1.Attributes.Count != 0)
                                    {
                                        dr["Duration"] = node1.Attributes["Duration"].Value.ToString();
                                    }
                                    break;
                                }
                            case "OrderBy":
                                {
                                    dr["OrderBy"] = node1.InnerText;
                                    break;
                                }
                            case "PerformBy":
                                {
                                    dr["PerformBy"] = node1.InnerText;
                                    break;
                                }
                            case "English":
                                {
                                    dr["English"] = node1.InnerText;
                                    break;
                                }
                        }
                    }
                    dt.Rows.Add(dr);
                }
            }
            else
            {
                dt = null;
            }
            return dt;
        }

        private string RemoveTZ(string str)
        {
            str = str.Replace("T", " ");
            str = str.Replace("Z", "");
            return str;
        }

    }
}
